<?php
include_once(_CORE_PATH. "mapper_prototype.php");
include_once(_APP_PATH. "business/articles/article_entry_entity.php");
include_once(_APP_PATH. "business/articles/article_filter.php");
class ArticleEntryMapper extends mapper_prototype
{
	private $_blogId = 0;
	private $_source;
	
	public function __construct($blogId)
	{
		parent::__construct('articles');
		$this->_blogId = (int)$blogId;
		
		$this->_source = MozgSource::get('default');
	}	
	
	static public function GetInstance($blogId)
	{
		return new self($blogId);		
	}

	public function GetById($id)
	{
		return $this->GetByProperty('mae.id', (int)$id);	
	}

	public function GetByUrl($url)
	{
		return $this->GetByProperty('mae.url', $this->_source->quote($url));	
	}	
	
	protected function GetByProperty($propName, $propValue)
	{
          return $this->_source->query("SELECT
													mae.id,
													mae.title,
													mae.url,
													mae.brief,
													mae.content,
													mae.date_published publishDate,
													mae.comments_num commentsNum,
													mae.tags,
													mae.blog_id blogId,
													mae.is_main isMain,
													mae.categories_norm categoriesNorm,

													mm.id media_id,
													mm.title media_title,
													mm.description media_description,
													mm.tags media_tags,
													mm.file media_file,
													mm.type media_type,
													mm.length media_length,
													mm.thumbnail media_thumbnail,
													mm.status media_status,
													mm.date_created media_dateCreated,
													mm.date_published media_publishDate,
													mm.filesize media_filesize,
													mm.is_blocked media_isBlocked
						                     FROM
						                     	%%article_entry%% mae 
						                     		LEFT JOIN %%media%% mm ON(mae.media_id = mm.id)	
						                     WHERE
						                     	" .$propName. " = " . $propValue . " and
						                     	mae.blog_id = " .$this->_blogId. "
						                    ")->fetchObject('ArticleEntryEntity');			
	}
	
	private function GetSqlFromFilter(ArticleEntryFilter $filter)
	{
		$sqlFilter = array();
		
		$tagsArr = $this->GetTagsArrFromStr($filter->GetTags());
		if (sizeof($tagsArr)) 
		{
			$searchStr = '';
			foreach ($tagsArr as $tag)
			{
				$searchStr .= '+",' .trim($this->_source->quote($tag), "'"). '," ';
			}
			$sqlFilter['where'][] = "MATCH(mae.tags_norm) AGAINST('" .$searchStr. "' IN BOOLEAN MODE)";
		}
		
		if ($filter->GetFromDate())
		{
			$sqlFilter['where'][] = 'mae.date_published >= ' .$this->_source->quote($filter->GetFromDate('Y-m-d H:i'));
		}
		
		if ($filter->GetToDate())
		{
			$sqlFilter['where'][] = 'mae.date_published <= ' .$this->_source->quote($filter->GetToDate('Y-m-d H:i'));
		}		

		if ($filter->GetCategory() && !$this->_blogId)
		{
			$sqlFilter['where'][] = 'mac.category_id = ' .(int)$filter->GetCategory();
		}			
		
		if (sizeof($sqlFilter['where'])) $sqlFilter['where'] = implode(' and ', $sqlFilter['where']);
		
		return $sqlFilter;
	}

	private $_totalRows = 0;
	
	public function GetTotalEntries()
	{
		return $this->_totalRows;		
	}
	
	public function GetEntries($paginator, ArticleEntryFilter $filter = null)
	{
		$sqlFilter = '';
		if ($filter)
		{
			$sqlFilter = $this->GetSqlFromFilter($filter);			
		}		

        $stmt = $this->_source->query("SELECT SQL_CALC_FOUND_ROWS
												mae.id,
												mae.title,
												mae.url,
												mae.brief,
												mae.content,
												mae.date_published publishDate,
												mae.comments_num commentsNum,
												mae.tags,
												mae.blog_id blogId,
												mae.is_main isMain,
												mae.categories_norm categoriesNorm,

												mm.id media_id,
												mm.title media_title,
												mm.description media_description,
												mm.tags media_tags,
												mm.file media_file,
												mm.type media_type,
												mm.length media_length,
												mm.thumbnail media_thumbnail,
												mm.status media_status,
												mm.date_created media_dateCreated,
												mm.date_published media_publishDate,
												mm.filesize media_filesize,
												mm.is_blocked media_isBlocked												
					                     FROM
					                     	%%article_entry%% mae
					                     		LEFT JOIN %%media%% mm ON(mae.media_id = mm.id)
					                     		" .(($filter->GetCategory() && !$this->_blogId) ? "INNER JOIN %%article_category%% mac on (mae.id = mac.entry_id)" : "" ) . "
					                     WHERE
					                     	mae.blog_id=" .$this->_blogId. "
					                     	" .($sqlFilter['where'] ? " and (" .$sqlFilter['where']. ")" : ""). "	
					                     ORDER BY mae.date_published DESC
					                     LIMIT " . $paginator->GetSqlLimit() . "	
					                     	");
        
        $this->_totalRows = $this->_source->query("SELECT FOUND_ROWS()")->fetchColumn();
        
	    $stmt->setFetchMode(PDO::FETCH_CLASS, 'ArticleEntryEntity');
        return $stmt->fetchAll();	          		
	}	
	
	public function GetThemeEntries($entryId)
	{
        $stmt = $this->_source->query("SELECT
												mae.id,
												mae.title,
												mae.url,
												mae.brief,
												mae.content,
												mae.date_published publishDate,
												mae.comments_num commentsNum,
												mae.tags,
												mae.blog_id blogId,
												mae.is_main isMain,
												mae.categories_norm categoriesNorm,

												mm.id media_id,
												mm.title media_title,
												mm.description media_description,
												mm.tags media_tags,
												mm.file media_file,
												mm.type media_type,
												mm.length media_length,
												mm.thumbnail media_thumbnail,
												mm.status media_status,
												mm.date_created media_dateCreated,
												mm.date_published media_publishDate,
												mm.filesize media_filesize,
												mm.is_blocked media_isBlocked													
					                     FROM
					                     	%%article_entry%% mae
					                     		LEFT JOIN %%media%% mm ON(mae.media_id = mm.id)
					                     		INNER JOIN %%article_theme_entries%% mate
					                     WHERE
					                     	mae.blog_id=" .$this->_blogId. " and
					                     	mae.id = mate.theme_entry_id and
					                     	mate.entry_id = " .(int)$entryId. "	
					                     ORDER BY mae.date_published DESC	
					                     ");

	    $stmt->setFetchMode(PDO::FETCH_CLASS, 'ArticleEntryEntity');
        return $stmt->fetchAll();	          		
	}	
	
	public function GetEntryFilter()
	{
		return new ArticleEntryFilter();
	} 	
	
	public function GetNew()
	{
		$obj = new ArticleEntryEntity();
		$obj->SetBlogId($this->_blogId);
		$obj->SetPublishDate('');
		return $obj;
	} 
		
	public function Save(ArticleEntryEntity $obj)
	{
		if (!$this->Prepare($obj)) return false;
		
		if ($obj->GetId())
		{
			return $this->Update($obj);		
		}

		return $this->Insert($obj);
	}
	
	private function Insert(ArticleEntryEntity $obj)
	{		
		$tagsArr = $this->GetTagsArrFromStr($obj->GetTags());
		$tagsNorm = $this->GetTagsNorm($tagsArr);
		
		$num = $this->_source->exec("insert into %%article_entry%%(title, url, brief, content, date_updated, date_published, tags, blog_id, media_id, is_main, tags_norm, categories_norm)
			            values(
					              " .$this->_source->quote($obj->GetTitle()). ",
					              " .(!is_null($obj->GetUrl()) ? $this->_source->quote($obj->GetUrl()) : 'NULL'). ",
					              " .$this->_source->quote($this->Libs()->str->EscapeAbsoluteUrls($obj->GetBrief())). ",
					              " .$this->_source->quote($this->Libs()->str->EscapeAbsoluteUrls($obj->GetContent())). ",
					              NOW(),
					              " .$this->_source->quote($obj->GetPublishDate('Y-m-d H:i')). ",
					              " .$this->_source->quote($obj->GetTags()). ",
					              " .$this->_blogId. ",
					              " .(int)$obj->GetMediaFile()->GetId(). ",
					              " .($obj->GetIsMain() ? 1 : 0). ",
					              " .$this->_source->quote($tagsNorm). ",
					              " .$this->_source->quote($obj->GetCategoriesNorm()). "
			            )
			");		

		if ($num)
		{
			$obj->SetId($this->_source->lastInsertID());
			$this->SetEntryTags($obj->GetId(), $tagsArr);
			return true;
		}
			
		return false;
	}
	
	private function Update(ArticleEntryEntity $obj)
	{
		$tagsArr = $this->GetTagsArrFromStr($obj->GetTags());
		$tagsNorm = $this->GetTagsNorm($tagsArr);		
		
		$num = (bool)$this->_source->exec("update %%article_entry%%
										      set
									              title = " .$this->_source->quote($obj->GetTitle()). ",
									              url = " .(!is_null($obj->GetUrl()) ? $this->_source->quote($obj->GetUrl()) : 'NULL'). ",
									              brief = " .$this->_source->quote($this->Libs()->str->EscapeAbsoluteUrls($obj->GetBrief())). ",
									              content = " .$this->_source->quote($this->Libs()->str->EscapeAbsoluteUrls($obj->GetContent())). ", 
									              date_updated = NOW(), 
									              date_published = " .$this->_source->quote($obj->GetPublishDate('Y-m-d H:i')). ",
									              tags = " .$this->_source->quote($obj->GetTags()). ", 
									              media_id = " .(int)$obj->GetMediaFile()->GetId(). ", 
									              is_main = " .($obj->GetIsMain() ? 1 : 0). ",
									              tags_norm = " .$this->_source->quote($tagsNorm). ",
									              categories_norm = " .$this->_source->quote($obj->GetCategoriesNorm()). "
										      where 
										      	 id=" .$obj->GetId(). " and
										      	 blog_id=" .$this->_blogId. "
									     ");		
		
		if ($num)
		{
			$this->SetEntryTags($obj->GetId(), $tagsArr);
			$this->SetEntryCategories($obj->GetId(), $obj->GetCategories());
			return true;
		}
	
		return false;
	}	

	private function SetEntryCategories($entryId, array $cats)
	{
		$cats = array_unique(array_map('intval', $cats));
		$entryId = (int)$entryId;
		
		$this->_source->exec("delete 
									from 
										%%article_category%%
					            	where
					            		entry_id = " .$entryId );

		if (sizeof($cats))
		{	
			$insSql = '';
			foreach ($cats as $catId) 
			{
				$insSql .= '(' .$entryId. ', ' .$catId. '),';										
			}
			
			$this->_source->exec("insert ignore into %%article_category%%(entry_id, category_id)
				            values
				            " .rtrim($insSql, ','));					
		}		
	}
	
	private function GetTagsNorm(array $tagsArr)
	{
		$tagsNorm = ',';
		foreach ($tagsArr as $tag)
		{
			$tagsNorm .= $tag. ',';
		}

		return $tagsNorm;
	}	
	
	public function GetTagsArrFromStr($tagsList)
	{
		return array_filter(array_unique(array_map('trim', explode(',', $tagsList))), array($this, 'CheckTagName'));		
	}
	
	private function SetEntryTags($entryId, $tagsArr = '')
	{
		$entryId = (int)$entryId;
		$this->_source->exec("delete 
									from 
										%%article_entry_tag%%
					            	where
					            		entry_id = " .$entryId );

		if (!is_array($tagsArr)) $tagsArr = $this->GetTagsArrFromStr($tagsArr);  

		if (sizeof($tagsArr))
		{
			$sqlPreparedTags = array_map(array($this->_source, 'quote'), $tagsArr);
			
			$this->_source->exec("insert ignore into %%tag%%(name)
				            values
				            (" .implode("),(", $sqlPreparedTags). ")");	
			
			$foundTags = $this->_source->query("SELECT
														id							   
								                     FROM
								                     	%%tag%%	
								                     WHERE
								                     	name in (" .implode(",", $sqlPreparedTags). ")
								                     ")->fetchAll(PDO::FETCH_COLUMN);		
			if (sizeof($foundTags))
			{
				$insSql = '';
				foreach ($foundTags as $tagId) 
				{
					$insSql .= '(' .$entryId. ', ' .$tagId. '),';										
				}
				
				$this->_source->exec("insert ignore into %%article_entry_tag%%(entry_id, tag_id)
					            values
					            " .rtrim($insSql, ','));					
			}
		}		
 		      					
	}
	
	public function CheckTagName($name)
	{
		return $this->Libs()->str->Len($name) > 1;				
	}
	
	public function SaveThemeEntries($entryId, array $themeEntryIds = array())
	{
		$entryId = (int)$entryId;
		$this->_source->exec("delete 
									from 
										%%article_theme_entries%%
					            	where
					            		entry_id = " .$entryId );

		$this->AddThemeEntries($entryId, $themeEntryIds);		
	}
	
	public function AddThemeEntries($entryId, array $themeEntryIds = array())
	{
		$entryId = (int)$entryId;		
		$themeEntryIds = array_diff(array_map('intval', $themeEntryIds), array($entryId));
		
		if (!sizeof($themeEntryIds)) return false;
		
		$insSql = '';
		foreach ($themeEntryIds as $themeEntryId) 
		{
			$insSql .= '(' .$entryId. ', ' .$themeEntryId. '),';
		}
		
		$this->_source->exec("insert ignore %%article_theme_entries%%(entry_id, theme_entry_id)
			            values
			            " .rtrim($insSql, ','));	
					
		return true;
	}
	
	public function DelThemeEntry($entryId, $themeEntryId)
	{
        return (bool)$this->_source->exec("DELETE 
        											mate
		        								FROM 
		        									%%article_theme_entries%% mate
		        								WHERE 
		        									mate.entry_id=" .(int)$entryId. " and
		        									mate.theme_entry_id=" .(int)$themeEntryId. "
		        								");		
	}
	
	private function Prepare(ArticleEntryEntity $obj)
	{
		if (!$this->Libs()->str->Len($obj->GetTitle()))
		{
			return $this->SetErrorMessage('Введите заголовок записи');			
		}

		if (!$this->_blogId)
		{
			if (!strlen($obj->GetUrl())) $obj->SetUrl($this->TrunslateUrl($obj->GetTitle()));
			
			if (!preg_match('/^[a-z_\-0-9]+$/uis', $obj->GetUrl())) 
	        {
	            return $this->SetErrorMessage('Неправильный url');
	        }	       	
	
			if ($this->CheckUrl($obj->GetId(), $obj->GetUrl()))
			{
				return $this->SetErrorMessage('Такой Url уже существует');			
			}
		}
		else
		{
			$obj->SetIsMain(0);
			$obj->SetUrl(null);			
			$obj->SetCategories(array());			
		}		
		
		$obj->SetBrief($this->ClearText($obj->GetBrief()));
		$obj->SetContent($this->ClearText($obj->GetContent()));
		
        return true;
	}

    private function CheckUrl($id, $url)
    {
		$obj = $this->GetByUrl($url);	
        return $obj && ($obj->GetId() != $id);
    }  	
	
    protected function TrunslateUrl($url)
    {
        return $this->Libs()->str->GetTraslit($url);
    }     
    
    private function ClearText($text)
    {
        return $this->Libs()->minifier->MinifyHTML($this->Libs()->purifier->purifyHTML($text));
    }	
	
    public function Delete($id)
    {

        $num = (bool)$this->_source->exec("DELETE 
        											mae, maet, mate, maec, mac
		        								FROM 
		        									%%article_entry%% mae
		        										left join %%article_entry_tag%% maet on (mae.id = maet.entry_id)
		        										left join %%article_theme_entries%% mate on (mae.id = mate.entry_id)
		        										left join %%article_entry_comment%% maec on (mae.id = maec.entry_id)
		        										left join %%article_category%% mac on (mae.id = mac.entry_id)
		        								WHERE 
		        									mae.id=" .(int)$id. " and
		        									mae.blog_id=" .$this->_blogId. "
		        								");
    	if ($num)
    	{	
    		return true;
    	}
    	return false;    
    }  	
	
    public function DeleteAll()
    {
        $num = (bool)$this->_source->exec("DELETE 
        											mae, maet, mate, maec, mac
		        								FROM 
		        									%%article_entry%% mae
		        										left join %%article_entry_tag%% maet on (mae.id = maet.entry_id)
		        										left join %%article_theme_entries%% mate on (mae.id = mate.entry_id)
		        										left join %%article_entry_comment%% maec on (mae.id = maec.entry_id)
		        										left join %%article_category%% mac on (mae.id = mac.entry_id)
		        								WHERE 
		        									mae.blog_id=" .$this->_blogId. "
		        								");
    	if ($num)
    	{		
    		return true;
    	}
    	return false;
    } 

    public function GetTagsByName($name, $maxRecords = 50)
    {
		if ($this->Libs()->str->Len($name) < 2) return array();
        
		$sqlName = str_replace('%', '\%', $this->_source->quote($name));
		$sqlName = rtrim($sqlName, "'")."%'";
		
		return $this->_source->query("SELECT
												name	
							                 FROM 
							                 	%%tag%%	
							                 WHERE
							                 	name LIKE " .$sqlName. "	              																 
							                 ORDER by name
							                 LIMIT 0, " .(int)$maxRecords. "
							               ")->fetchAll(PDO::FETCH_COLUMN);	
    }
}	